﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Tedds_Tool.Compilers.LEET;
using CommonInterfaces.Models;

namespace Tedds_Tool.Instructions
{
    public abstract class Instruction : IInstruction
    {
        protected String [] args;

        public Instruction(String [] str)
        {
            args = str;
        }

        public override String ToString()
        {
            return String.Join<String>(" | ", args);
        }

        public static String[] ParseInstruction(String str)
        {
            List<String> ret = new List<String>();
            bool inQuote = false;
            int start = 0;
            int length = 0;

            while (start < str.Length)
            {
                if (start + length >= str.Length)
                {
                    ret.Add(str.Substring(start).Trim());
                    start = str.Length;
                } 
                else if (!inQuote && str[start + length] == '|')
                {
                    ret.Add(str.Substring(start, length).Trim());
                    start += length + 1;
                    length = 0;
                }
                else if (str[start + length] == '"')
                {
                    inQuote = !inQuote;
                    length++;
                }
                else
                {
                    length++;
                }
            }
            
            return ret.ToArray();
        }

        public static Instruction apply(String str)
        {
            Instruction ret = null;
            string [] args = ParseInstruction( str );
            switch (args[0].Trim())
            {
                case "ACTION": ret = Action.apply(args); break;
                case "TARGET": ret = new Target(args); break;
                case "PAUSE": ret = new Pause(args); break;
                case "START": ret = new Start(args); break;
                case "OPTIONS": ret = new Options(args); break;
                case "COMMENT": ret = new Comment(args); break;
                case "BREAKPOINT": ret = new DemoBreakpoint(args); break;
                case "RULEBASE": ret = new RuleBase(args); break;
                case "EMBED": ret = new EmbeddedScript(args); break;
                case "MOUSE_EVENT": ret = MouseAction.apply(args); break;
                //case "REFRESH": ret = new Refresh(); break;
            }
            if (ret == null)
                throw new InvalidInstructionException();
            return ret;
        }

        public virtual StringBuilder RenderMethod(int i, AssertionSet asserts)
        {
            if (Valid)
            {
                StringBuilder ret = new StringBuilder();
                ret.Append("        private AutomationActionResult ").Append(RenderedMethodName(i)).Append("()\r\n");
                ret.Append("        {\r\n");
                ret.Append("            try\r\n");
                ret.Append("            {\r\n");
                ret.Append(MethodBody(asserts));
                ret.Append("            }\r\n");
                ret.Append("            catch (Exception e)\r\n");
                ret.Append("            {\r\n");
                ret.Append("                String detail = \"error.Message = \" + e.Message + \", error.GetType = \" + e.GetType();\r\n");
                ret.Append("                return new AutomationActionResult(new Exception(detail, e));\r\n");
                ret.Append("            }\r\n");
                ret.Append("        }\r\n\r\n");
                return ret;
            }
            else
            {
                throw new InvalidInstructionException(Errors.ToString());
            }
        }

        public virtual String RenderMethodCall(int i)
        {
            return RenderedMethodName(i) + "()";
        }

        protected void AddErrorMsg(String msg)
        {
            errors.Add(msg);
        }

        public virtual bool Valid { get { return Errors.Count <= 0; } }

        private IList<String> errors = new List<String>();
        public virtual IList<String> Errors { get { return errors; } }

        protected internal abstract String MethodBody(AssertionSet asserts);
        protected abstract String RenderedMethodName(int i);
        public abstract String PlainText();

        public override bool Equals(Object obj)
        {
            if(obj == null || !(obj is Instruction) )
            {
                return false;
            }
            Instruction other = (Instruction)obj;
            return args.Equals(other.args);
        }

        public override int GetHashCode()
        {
            return args.GetHashCode();
        }

    }
}
